\subsubsection{\NonOptimizingKeilVI (\ARMMode)}

Для начала скомпилируем наш пример в Keil:

\begin{lstlisting}
armcc.exe --arm --c90 -O0 1.c 
\end{lstlisting}

\myindex{\IntelSyntax}
Компилятор \IT{armcc} генерирует листинг на ассемблере в формате Intel.
Этот листинг содержит некоторые высокоуровневые макросы, связанные с ARM
\footnote{например, он показывает инструкции \PUSH/\POP, отсутствующие в режиме ARM},
а нам важнее увидеть инструкции \q{как есть}, так что посмотрим скомпилированный результат в \IDA.

\begin{lstlisting}[caption=\NonOptimizingKeilVI (\ARMMode) \IDA,style=customasmARM]
.text:00000000             main
.text:00000000 10 40 2D E9    STMFD   SP!, {R4,LR}
.text:00000004 1E 0E 8F E2    ADR     R0, aHelloWorld ; "hello, world"
.text:00000008 15 19 00 EB    BL      __2printf
.text:0000000C 00 00 A0 E3    MOV     R0, #0
.text:00000010 10 80 BD E8    LDMFD   SP!, {R4,PC}

.text:000001EC 68 65 6C 6C+aHelloWorld  DCB "hello, world",0    ; DATA XREF: main+4
\end{lstlisting}

В вышеприведённом примере можно легко увидеть, что каждая инструкция имеет размер 4 байта.
Действительно, ведь мы же компилировали наш код для режима ARM, а не Thumb.

\myindex{ARM!\Instructions!STMFD}
\myindex{ARM!\Instructions!POP}
Самая первая инструкция, \INS{STMFD SP!, \{R4,LR\}}\footnote{\ac{STMFD}},
работает как инструкция \PUSH в x86, записывая значения двух регистров (\Reg{4} и \ac{LR}) в стек.
Действительно, в выдаваемом листинге на ассемблере компилятор \IT{armcc} для упрощения указывает здесь инструкцию
\INS{PUSH \{r4,lr\}}.
Но это не совсем точно, инструкция \PUSH доступна только в режиме Thumb, поэтому,
во избежание путаницы, я предложил работать в \IDA.

Итак, эта инструкция уменьшает \ac{SP}, чтобы он указывал на место в стеке, свободное для записи
новых значений, затем записывает значения регистров \Reg{4} и \ac{LR} 
по адресу в памяти, на который указывает измененный регистр \ac{SP}.

Эта инструкция, как и инструкция \PUSH в режиме Thumb, может сохранить в стеке одновременно несколько значений регистров, что может быть очень удобно.
Кстати, такого в x86 нет.
Также следует заметить, что \TT{STMFD}~--- генерализация инструкции \PUSH (то есть расширяет её возможности), потому что может работать с любым регистром, а не только с \ac{SP}.
Другими словами, \TT{STMFD} можно использовать для записи набора регистров в указанном месте памяти.

\myindex{\PICcode}
\myindex{ARM!\Instructions!ADR}
Инструкция \INS{ADR R0, aHelloWorld} прибавляет или отнимает значение регистра \ac{PC} к смещению, где хранится строка
\TT{hello, world}.
Причем здесь \ac{PC}, можно спросить? Притом, что это так называемый \q{\PICcode}
\footnote{Читайте больше об этом в соответствующем разделе ~(\myref{sec:PIC})}.
Он предназначен для исполнения будучи не привязанным к каким-либо адресам в памяти.
Другими словами, это относительная от \ac{PC} адресация.
В опкоде инструкции \TT{ADR} указывается разница между адресом этой инструкции и местом, где хранится строка.
Эта разница всегда будет постоянной, вне зависимости от того, куда был загружен \ac{OS} наш код.
Поэтому всё, что нужно~--- это прибавить адрес текущей инструкции (из \ac{PC}), чтобы получить текущий абсолютный адрес нашей Си-строки.

\myindex{ARM!\Registers!Link Register}
\myindex{ARM!\Instructions!BL}
Инструкция \INS{BL \_\_2printf}\footnote{Branch with Link} вызывает функцию \printf.
Работа этой инструкции состоит из двух фаз:

\begin{itemize}
\item записать адрес после инструкции \INS{BL} (\TT{0xC}) в регистр \ac{LR};
\item передать управление в \printf, записав адрес этой функции в регистр \ac{PC}.
\end{itemize}

Ведь когда функция \printf закончит работу, нужно знать, куда вернуть управление, поэтому закончив работу, всякая функция передает управление по адресу, записанному в регистре \ac{LR}.

В этом разница между \q{чистыми} \ac{RISC}-процессорами вроде ARM и \ac{CISC}-процессорами как x86,
где адрес возврата обычно записывается в стек ~(\myref{sec:stack}).

Кстати, 32-битный абсолютный адрес (либо смещение) невозможно закодировать в 32-битной инструкции \INS{BL}, в ней есть место только для 24-х бит.
Поскольку все инструкции в режиме ARM имеют длину 4 байта (32 бита) и инструкции могут находится только по адресам кратным 4, то последние 2 бита (всегда нулевых) можно не кодировать.
В итоге имеем 26 бит, при помощи которых можно закодировать $current\_PC \pm{} \approx{}32M$.

\myindex{ARM!\Instructions!MOV}
Следующая инструкция \INS{MOV R0, \#0}\footnote{Означает MOVe}
просто записывает 0 в регистр \Reg{0}.
Ведь наша Си-функция возвращает 0, а возвращаемое значение всякая функция оставляет в \Reg{0}.

\myindex{ARM!\Registers!Link Register}
\myindex{ARM!\Instructions!LDMFD}
\myindex{ARM!\Instructions!POP}
Последняя инструкция \INS{LDMFD SP!, {R4,PC}}\footnote{\ac{LDMFD}~--- это инструкция, обратная \ac{STMFD}}.
Она загружает из стека (или любого другого места в памяти) значения для сохранения их в \Reg{4} и \ac{PC}, увеличивая \glslink{stack pointer}{указатель стека} \ac{SP}.
Здесь она работает как аналог \POP.\\
N.B. Самая первая инструкция \TT{STMFD} сохранила в стеке \Reg{4} и \ac{LR}, а \IT{восстанавливаются} во время исполнения \TT{LDMFD} регистры \Reg{4} и \ac{PC}.

Как мы уже знаем, в регистре \ac{LR} обычно сохраняется адрес места, куда нужно всякой функции вернуть управление.
Самая первая инструкция сохраняет это значение в стеке, потому что наша функция \main позже будет сама пользоваться этим регистром в момент вызова \printf.
А затем, в конце функции, это значение можно сразу записать прямо в \ac{PC}, таким образом, передав управление туда, откуда была вызвана наша функция.

Так как функция \main обычно самая главная в \CCpp, управление будет возвращено в загрузчик \ac{OS}, либо куда-то в \ac{CRT} 
или что-то в этом роде.

Всё это позволяет избавиться от инструкции \INS{BX LR} в самом конце функции.

\myindex{ARM!DCB}
\TT{DCB}~--- директива ассемблера, описывающая массивы байт или ASCII-строк, аналог директивы DB в x86-ассемблере.

